package com.nightpiter.view.drawer.simple;

import java.util.ArrayList;

import com.nightpiter.model.objects.Coordinates;
import com.nightpiter.model.objects.Station;
import com.nightpiter.model.objects.Transfer;
import com.nightpiter.model.objects.Tunnel;
import com.nightpiter.view.Calculator;
import com.nightpiter.view.drawer.ClassicMetroDrawer;
import com.nightpiter.view.drawer.Property;
import com.nightpiter.view.drawer.Property.Type;
import com.nightpiter.view.graphics.Canvas;
import com.nightpiter.view.graphics.Color;
import com.nightpiter.view.graphics.Paint;
import com.nightpiter.view.graphics.PointF;
import com.nightpiter.view.graphics.Point;
import com.nightpiter.view.graphics.RectF;
import com.nightpiter.view.graphics.abstaractions.AbstractPaint.Style;

/**
 * a simple Drawer realization
 * 
 * just shows how it works.
 * 
 * @author Ilya Shurigyn (honorik@gmail.com)
 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
 * @author Zufar Fakhurtdinov (zufar.fakhurtdinov@gmail.com)
 */
public class SimpleMetroDrawer extends ClassicMetroDrawer {

	private Paint sPaint = new Paint();

	private Paint lPaint = new Paint();

	private Paint cPaint = new Paint();

	private PointF pointF = new PointF();

	private static final float STATION_RADIUS = 4;

	public boolean internalPrepare() {

		this.properties = new Property[3];
		String[] BoolSt = new String[] { "true", "false" };
		Property p = new Property("draw tunels", Type.CHECKBOX, BoolSt);
		p.setCurrentValue("true");

		Property p1 = new Property("draw stations", Type.CHECKBOX, BoolSt);
		p1.setCurrentValue("true");

		String[] BoolSt1 = new String[] { "true", "false" };
		Property p2 = new Property("draw all?", Type.DROPDOWN_LIST, BoolSt1);
		p2.setCurrentValue("true");

		properties[0] = p;
		properties[1] = p1;
		properties[2] = p2;

		sPaint.setAntiAlias(true);
		sPaint.setColor(Color.WHITE);
		sPaint.setStrokeWidth(STATION_RADIUS);
		sPaint.setStrokeCap(Paint.Cap.ROUND);

		lPaint.setAntiAlias(true);
		lPaint.setColor(Color.WHITE);
		lPaint.setStrokeWidth(10);
		lPaint.setStrokeCap(Paint.Cap.ROUND);

		cPaint.setAntiAlias(true);
		cPaint.setColor(Color.GRAY);
		cPaint.setStrokeWidth(20);
		cPaint.setStrokeCap(Paint.Cap.ROUND);

		return super.internalPrepare();
	}

	public Property[] getProperties() {
		return properties;
	}

	public void internalDrawMetro(Canvas canvas, Calculator calculator) {

		/*
		 * sPaint.setColor(Color.BLACK); sPaint.setStrokeWidth(STATION_RADIUS);
		 * canvas.drawText("" + this.pr[0].getCurrentValue() + "\n " +
		 * this.pr[1].getCurrentValue() + "\n " + this.pr[2].getCurrentValue(),
		 * 15, 13,sPaint); sPaint.setColor(Color.WHITE);
		 */
		if (isTrue(this.properties[0])) {
			for (Tunnel tunnel : modelData.getTunnels()) {
				this.drawTunnel(tunnel, canvas, calculator);
			}
		}
		if (isTrue(this.properties[1])) {
			for (Station station : modelData.getStations()) {
				this.drawStation(station, canvas, calculator);
			}
		}
		if (isTrue(this.properties[2])) {
			if (!isTrue(this.properties[0])) {
				for (Tunnel tunnel : modelData.getTunnels()) {
					this.drawTunnel(tunnel, canvas, calculator);
				}
			}
			if (!isTrue(this.properties[1])) {
				for (Station station : modelData.getStations()) {
					this.drawStation(station, canvas, calculator);
				}
			}
		}

	}

	private void drawStation(Station station, Canvas canvas,
			Calculator calculator) {
		calculator.convert(station, pointF);
		canvas.drawPoint(pointF.x, pointF.y, sPaint);
		canvas
				.drawText(station.getName(), pointF.x + 10, pointF.y + 10,
						cPaint);
	}

	private void drawTunnel(Tunnel tunnel, Canvas canvas, Calculator calculator) {

		if (tunnel.isSmooth() == false)
			drawTunnelWithoutSmoothing(tunnel, canvas, calculator);
		else
			drawTunnelWithEllipseSmoothing(tunnel, canvas, calculator);
		// drawTunnelWithRoundSmoothing(tunnel, canvas, calculator);

	}

	private void drawTunnelWithoutSmoothing(Tunnel tunnel, Canvas canvas,
			Calculator calculator) {
		float startX, startY, stopX, stopY;
		calculator.convert(tunnel.getFromStation(), pointF);
		startX = pointF.x;
		startY = pointF.y;
		calculator.convert(tunnel.getToStation(), pointF);
		stopX = pointF.x;
		stopY = pointF.y;
		lPaint.setColor(tunnel.getLine().getColor());
		canvas.drawLine(startX, startY, stopX, stopY, lPaint);
	}

	private void drawArcWithBottomFixed(Tunnel tunnel, Canvas canvas,
			Calculator calculator) {
		int fromX, fromY, toX, toY;
		int left, top, right, bottom, startAngle, sweepAngle;
		fromX = tunnel.getFromStation().getPosition().x;
		fromY = tunnel.getFromStation().getPosition().y;
		toX = tunnel.getToStation().getPosition().x;
		toY = tunnel.getToStation().getPosition().y;
		if (toY < fromY) {
			bottom = fromY;
			top = 2 * toY - fromY;
			if (toX < fromX) {
				left = toX;
				right = 2 * fromX - toX;
				startAngle = 90;
				sweepAngle = 90;
			} else {
				right = toX;
				left = 2 * fromX - toX;
				startAngle = 0;
				sweepAngle = 90;
			}
		} else {
			bottom = toY;
			top = 2 * fromY - toY;
			if (toX < fromX) {
				right = fromX;
				left = 2 * toX - fromX;
				startAngle = 0;
				sweepAngle = 90;
			} else {
				left = fromX;
				right = 2 * toX - fromX;
				startAngle = 90;
				sweepAngle = 90;
			}
		}
		PointF leftTop = new PointF(), rightBottom = new PointF();
		calculator.convert(new Coordinates(left, top), leftTop);
		calculator.convert(new Coordinates(right, bottom), rightBottom);
		RectF oval = new RectF(leftTop.x, leftTop.y, rightBottom.x,
				rightBottom.y);
		canvas.drawArc(oval, startAngle, sweepAngle, lPaint);
	}

	private void drawArcWithTopFixed(Tunnel tunnel, Canvas canvas,
			Calculator calculator) {
		int fromX, fromY, toX, toY;
		int left, top, right, bottom, startAngle, sweepAngle;
		fromX = tunnel.getFromStation().getPosition().x;
		fromY = tunnel.getFromStation().getPosition().y;
		toX = tunnel.getToStation().getPosition().x;
		toY = tunnel.getToStation().getPosition().y;
		if (toY < fromY) {
			top = toY;
			bottom = 2 * fromY - toY;
			// 
			if (toX < fromX) {
				right = fromX;
				left = 2 * toX - fromX;
				startAngle = 270;
				sweepAngle = 90;
			} else {
				left = fromX;
				right = 2 * toX - fromX;
				startAngle = 180;
				sweepAngle = 90;
			}
		} else {
			top = fromY;
			bottom = 2 * toY - fromY;
			if (toX < fromX) {
				left = toX;
				right = 2 * fromX - toX;
				startAngle = 180;
				sweepAngle = 90;
			} else {
				right = toX;
				left = 2 * fromX - toX;
				startAngle = 270;
				sweepAngle = 90;
			}
		}
		PointF leftTop = new PointF(), rightBottom = new PointF();
		calculator.convert(new Coordinates(left, top), leftTop);
		calculator.convert(new Coordinates(right, bottom), rightBottom);
		RectF oval = new RectF(leftTop.x, leftTop.y, rightBottom.x,
				rightBottom.y);
		canvas.drawArc(oval, startAngle, sweepAngle, lPaint);
	}

	private void drawTunnelWithEllipseSmoothing(Tunnel tunnel, Canvas canvas,
			Calculator calculator) {
		int fromX, fromY, toX, toY;
		fromX = tunnel.getFromStation().getPosition().x;
		fromY = tunnel.getFromStation().getPosition().y;
		toX = tunnel.getToStation().getPosition().x;
		toY = tunnel.getToStation().getPosition().y;
		if ((fromY == toY) || (toX == fromX)) {
			drawTunnelWithoutSmoothing(tunnel, canvas, calculator);
			return;
		}

		lPaint.setColor(tunnel.getLine().getColor());
		lPaint.setStyle(Style.STROKE);
		Tunnel prevTunnel = null;
		for (Tunnel tempReverseTunnel : tunnel.getFromStation().getTunnels()) {
			if ((tempReverseTunnel.getToStation() != tunnel.getToStation())
					&& (tempReverseTunnel.getLine() == tunnel.getLine())) {
				for (Tunnel tempTunnel : tempReverseTunnel.getToStation()
						.getTunnels()) {
					if ((tempTunnel.getToStation() == tunnel.getFromStation())
							&& (tempTunnel.getLine() == tunnel.getLine())) {
						prevTunnel = tempTunnel;
						break;
					}
				}
			}
		}
		int prevToX, prevToY, prevFromX, prevFromY;
		if (prevTunnel != null) {
			prevToX = prevTunnel.getToStation().getPosition().x;
			prevToY = prevTunnel.getToStation().getPosition().y;
			prevFromX = prevTunnel.getFromStation().getPosition().x;
			prevFromY = prevTunnel.getFromStation().getPosition().y;
			if (prevToY == prevFromY) {
				if (prevToX > prevFromX) {
					if ((toY - fromY) * (toX - fromX) > 0)
						drawArcWithTopFixed(tunnel, canvas, calculator);
					else
						drawArcWithBottomFixed(tunnel, canvas, calculator);
				} else {
					if ((toY - fromY) * (toX - fromX) > 0)
						drawArcWithBottomFixed(tunnel, canvas, calculator);
					else
						drawArcWithTopFixed(tunnel, canvas, calculator);
				}
			} else if (prevToY > prevFromY) {
				if (prevToX == prevFromX) {
					drawArcWithBottomFixed(tunnel, canvas, calculator);
				} else {
					if (prevToX > prevFromX) {
						if (((toY - fromY) * (prevToX - prevFromX) > (toX - fromX)
								* (prevToY - prevFromY))
								&& (toY - fromY > 0) && (toX - fromX > 0))
							drawArcWithTopFixed(tunnel, canvas, calculator);
						else
							drawArcWithBottomFixed(tunnel, canvas, calculator);
					} else {
						if (((toY - fromY) * (prevToX - prevFromX) < (toX - fromX)
								* (prevToY - prevFromY))
								&& (toY - fromY > 0) && (toX - fromX < 0)) {
							drawArcWithTopFixed(tunnel, canvas, calculator);
						} else {
							drawArcWithBottomFixed(tunnel, canvas, calculator);
						}
					}
				}
			} else {
				if (prevToX == prevFromX)
					drawArcWithTopFixed(tunnel, canvas, calculator);
				else {
					if (prevToX > prevFromX) {
						if ((toY - fromY) * (prevToX - prevFromX) < (toX - fromX)
								* (prevToY - prevFromY)
								&& (toX - fromX > 0) && (toY - fromY < 0))
							drawArcWithBottomFixed(tunnel, canvas, calculator);
						else
							drawArcWithTopFixed(tunnel, canvas, calculator);
					} else {
						if ((toY - fromY) * (prevToX - prevFromX) > (toX - fromX)
								* (prevToY - prevFromY)
								&& (toX - fromX < 0) && (toY - fromY < 0)) {
							drawArcWithBottomFixed(tunnel, canvas, calculator);
						} else {
							drawArcWithTopFixed(tunnel, canvas, calculator);
						}
					}
				}
			}
		}
	}

	@SuppressWarnings("unused")
	private void drawTunnelWithRoundSmoothing(Tunnel tunnel, Canvas canvas,
			Calculator calculator) {
		PointF from = new PointF(tunnel.getFromStation().getPosition());
		PointF to = new PointF(tunnel.getToStation().getPosition());
		lPaint.setColor(tunnel.getLine().getColor());
		lPaint.setStyle(Style.STROKE);
		Tunnel prevTunnel = null;
		Tunnel nextTunnel = null;
		for (Tunnel tempReverseTunnel : tunnel.getFromStation().getTunnels()) {
			if ((tempReverseTunnel.getToStation() != tunnel.getToStation())
					&& (tempReverseTunnel.getLine() == tunnel.getLine())) {
				for (Tunnel tempTunnel : tempReverseTunnel.getToStation()
						.getTunnels()) {
					if ((tempTunnel.getToStation() == tunnel.getFromStation())
							&& (tempTunnel.getLine() == tunnel.getLine())) {
						prevTunnel = tempTunnel;
						break;
					}
				}
			}
		}
		for (Tunnel tempTunnel : tunnel.getToStation().getTunnels())
			if ((tempTunnel.getLine() == tunnel.getLine())
					&& (tempTunnel.getToStation() != tunnel.getFromStation())) {
				nextTunnel = tempTunnel;
				break;
			}
		if ((nextTunnel != null) && (prevTunnel != null)) {
			PointF prevFrom = new PointF(prevTunnel.getFromStation()
					.getPosition());
			PointF prevTo = new PointF(prevTunnel.getToStation().getPosition());
			PointF nextFrom = new PointF(nextTunnel.getFromStation()
					.getPosition());
			PointF nextTo = new PointF(nextTunnel.getToStation().getPosition());
			if (crossingOfLines(prevFrom, prevTo, nextFrom, nextTo) != null) {
				PointF crossingPoint = crossingOfLines(prevFrom, prevTo,
						nextFrom, nextTo);
				if ((crossingPoint != from) && (crossingPoint != to)) {
					PointF center = new PointF(), realCenter = new PointF();
					float radius, radiusReal;
					PointF otherPointOnRound = new PointF();
					PointF otherPointOnRoundReal = new PointF(), toReal = new PointF();
					PointF perpendicular = new PointF();
					PointF perpendicularOther = new PointF();

					if (crossingPoint.distanceTo(from) < crossingPoint
							.distanceTo(to)) {
						float tanAngleFrom;
						float tanAngleOther;
						radius = crossingPoint.distanceTo(from);
						otherPointOnRound.x = crossingPoint.x
								+ (to.x - crossingPoint.x) * radius
								/ (crossingPoint.distanceTo(to));
						otherPointOnRound.y = crossingPoint.y
								+ (to.y - crossingPoint.y) * radius
								/ (crossingPoint.distanceTo(to));
						calculator.convert(otherPointOnRound,
								otherPointOnRoundReal);
						calculator.convert(to, toReal);
						canvas.drawLine(otherPointOnRoundReal, toReal, lPaint);
						perpendicular.x = -(crossingPoint.y - from.y);
						perpendicular.y = (crossingPoint.x - from.x);
						perpendicular.add(from);
						perpendicularOther.x = -(to.y - crossingPoint.y);
						perpendicularOther.y = (to.x - crossingPoint.x);
						perpendicularOther.add(otherPointOnRound);
						center = crossingOfLines(from, perpendicular,
								otherPointOnRound, perpendicularOther);
						if (center != null) {
							calculator.convert(center, realCenter);
							radiusReal = realCenter
									.distanceTo(otherPointOnRoundReal);
							RectF oval = new RectF(realCenter.x - radiusReal,
									realCenter.y - radiusReal, realCenter.x
											+ radiusReal, realCenter.y
											+ radiusReal);
							float startAngle, finishAngle;
							if (from.x != center.x) {
								tanAngleFrom = (from.y - center.y)
										/ (from.x - center.x);
								if (from.x - center.x > 0) {
									startAngle = (float) ((180 / Math.PI) * Math
											.atan(tanAngleFrom));
								} else {
									startAngle = (float) (180 - (180 / Math.PI)
											* Math.atan(tanAngleFrom));
								}
							} else {
								if (from.y - center.y > 0) {
									startAngle = 90;
								} else {
									startAngle = 270;
								}
							}
							if (otherPointOnRound.x != center.x) {
								tanAngleOther = (otherPointOnRound.y - center.y)
										/ (otherPointOnRound.x - center.x);
								if (otherPointOnRound.x > center.x) {
									finishAngle = (float) ((180 / Math.PI) * Math
											.atan(tanAngleOther));
								} else {
									finishAngle = (float) (180 - (180 / Math.PI)
											* Math.atan(tanAngleOther));
								}
							} else {
								if (otherPointOnRound.y - center.y > 0) {
									finishAngle = 90;
								} else {
									finishAngle = 270;
								}
							}
							float sweepAngle;
							if ((otherPointOnRound.x - center.x)
									* (crossingPoint.y - center.y)
									- (crossingPoint.x - center.x)
									* (otherPointOnRound.y - center.y) < 0) {
								sweepAngle = finishAngle - startAngle;
								while (sweepAngle < 0) {
									sweepAngle += 360;
								}
								canvas.drawArc(oval, startAngle, sweepAngle,
										lPaint);
							} else {
								sweepAngle = startAngle - finishAngle;
								while (sweepAngle < 0) {
									sweepAngle += 360;
								}
								canvas.drawArc(oval, finishAngle, sweepAngle,
										lPaint);
							}
						}
					} else {

						/*
						 * otherPointOnRound.x = crossingPoint.x + (prevTo.x -
						 * prevFrom.x) (crossingPoint.distanceTo(from) /
						 * radius); otherPointOnRound.y = crossingPoint.y +
						 * (prevTo.y - prevFrom.y)
						 * (crossingPoint.distanceTo(from) / radius);
						 * calculator.convert(otherPointOnRound, startPoint);
						 * calculator.convert(from, finishPoint);
						 * canvas.drawLine(startPoint, finishPoint, lPaint);
						 * center.x = to.x + (otherPointOnRound.x -
						 * crossingPoint.x); center.y = to.y +
						 * (otherPointOnRound.y - crossingPoint.y); RectF oval =
						 * new RectF(center.x - radius, center.y - radius,
						 * center.x + radius, center.y + radius);
						 * canvas.drawArc(oval, 0, 360, lPaint);
						 */
					}
				}
			} else
				drawTunnelWithoutSmoothing(tunnel, canvas, calculator);
		} else
			drawTunnelWithoutSmoothing(tunnel, canvas, calculator);
	}

	private PointF crossingOfLines(PointF prevFrom, PointF prevTo,
			PointF nextFrom, PointF nextTo) {
		PointF result = new PointF();
		float a, b, c, d;
		if (prevFrom.y != prevTo.y) {
			a = (prevFrom.x - prevTo.x) / (prevFrom.y - prevTo.y);
			b = -prevTo.y * a + prevTo.x;
			if (nextFrom.y != nextTo.y) {
				c = (nextFrom.x - nextTo.x) / (nextFrom.y - nextTo.y);
				d = -nextTo.y * c + nextTo.x;
				if (a == c) {
					return null;
				}
				result.y = ((d - b) / (a - c));
				result.x = (a * result.y + b);
			} else {
				result.y = nextFrom.y;
				result.x = (a * result.y + b);
			}
		} else {
			if (nextFrom.y != nextTo.y) {
				c = (nextFrom.x - nextTo.x) / (nextFrom.y - nextTo.y);
				d = -nextTo.y * c + nextTo.x;
				result.y = prevFrom.y;
				result.x = (c * result.y + d);
			} else {
				return null;
			}
		}
		return result;
	}

	@Override
	public void selectStation(Station station, Canvas canvas,
			Calculator calculator) {
		calculator.convert(station, pointF);
		canvas.drawPoint(pointF.x, pointF.y, cPaint);
	}

	@Override
	public void showWay(ArrayList<Tunnel> selectedTunnels,
			ArrayList<Transfer> selectedTransfers,
			ArrayList<Station> selectedStations, Canvas canvas,
			Calculator calculator) {

	}

	@Override
	public Station[] searchStation(Point point, Calculator calculator) {
		Station station = super.classicSearchStation(point, calculator);
		if (station == null) {
			return new Station[0];
		}
		return new Station[] { station };
	}

	private boolean isTrue(Property p) {
		boolean res = false;
		if (p.getCurrentValue() != null) {
			if (p.getCurrentValue().contains("true")) {
				res = true;
			}
		}
		return res;

	}
}
